package graphicComponent;

import java.awt.Canvas;
import java.awt.Color;
import java.awt.Graphics;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;


/**
 * Class used to generate the 2D plot of test results
 * 
 * @author Andy Cullache
 */
public class Graphic2D extends Canvas
{

    private int maxXGraph = 100;
    private double maxYGraph = 100;
    private int varXGraph = 100;
    private double varYGraph = 100;
    private int[] valuesAxisX;
    
    /** Test result list, Integer=>variable (number of recommendations), Double => value of result of metrics     */
	private List<Map<Integer, Double>> listMap = new ArrayList<>();
	private Color[] colors = {Color.BLUE,Color.RED,Color.MAGENTA,Color.ORANGE,Color.DARK_GRAY
			,Color.GREEN,Color.LIGHT_GRAY,Color.GRAY,Color.PINK,Color.YELLOW,Color.CYAN};
    
	private float fPuntoXT[] = new float[30];
	private float fPuntoYT[] = new float[30];
	private float fPuntoZT[] = new float[30];

    private int iDisPantUser;
    private float fMinPantX;
    private float fMaxPantX;
    private float fMinPantY;
    private float fMaxPantY;
    private float var = 55;
    private float fConstanteX;
    private float fConstanteY;
    /** Independent variable (X axis)*/
    private List<Integer> keyAxisX;
    private int iNumPuntos;       	
    
    /**
	 * Constructor principal, receives as parameter values of the independent variable (X axis).
	 */
    public Graphic2D(int[] axisXValues)
    {
    	keyAxisX = new ArrayList<Integer>();
        this.valuesAxisX = axisXValues;
        int max = 0;
        for(int var : valuesAxisX){
        	keyAxisX.add(var);
        	if(var>max)
        		max = var;
        }
        Collections.sort(keyAxisX);
        maxXGraph = max;
        varXGraph = 100/maxXGraph;
        iNumPuntos = 29;
        this.iDisPantUser = 60;
    }
	
    /**
     * This method is called in response to a call to repaint. The canvas is first cleared by filling 
     * it with the background color, and then completely redrawn by calling this canvas's paint method.
     * 
     * @param objGraphic      The specified Graphics context.
     */
    @Override
    public void update(Graphics objGraphic)
    {
        paint( objGraphic );
    }

    /**
     * Most applications that subclass Canvas should override this method in order to perform some useful 
     * operation (typically, custom painting of the canvas). The default operation is simply to clear the canvas. 
     * 
     * @param objGraphic      The specified Graphics context.
     */
    @Override
    public void paint( Graphics objGraphic )
    {
        /* start the bottom of the screen*/
      	objGraphic.setColor(Color.WHITE);
      	objGraphic.fillRect(0,0,size().width, size().height);
        
        for (int iCont=0; iCont<iNumPuntos; iCont++)
        {
            float fPuntoX2D = (float) (iDisPantUser*fPuntoXT[iCont] ) / (iDisPantUser - fPuntoZT[iCont]);
	    float fPuntoY2D = (float) (iDisPantUser*fPuntoYT[iCont] ) / (iDisPantUser - fPuntoZT[iCont]);
            if (fPuntoX2D < fMinPantX) fMinPantX = fPuntoX2D;
            if (fPuntoX2D > fMaxPantX) fMaxPantX = fPuntoX2D;
	    if (fPuntoY2D < fMinPantY) fMinPantY = fPuntoY2D;
    	    if (fPuntoY2D > fMaxPantY) fMaxPantY = fPuntoY2D;
        }
      	fMaxPantX = var;
        fMinPantX = -1*var;
        fMaxPantY = var;
        fMinPantY = -1*var;
        fConstanteX = (float) getSize().width / (fMaxPantX-fMinPantX);
        fConstanteY = (float) getSize().height / (fMaxPantY-fMinPantY);

        vDraw(objGraphic, Color.BLACK, -47,47,0,-47, -47, 0);
        vDraw(objGraphic, Color.BLACK, -47,-47,0,47,-47,0);

        paintAxisX(objGraphic);
        paintAxisY(objGraphic);
        double a = this.maxXGraph;
        double auxX = 100/a;
        double b = this.maxYGraph;
        double auxY = 100/b;
        int color = 0;
        for(Map<Integer, Double> map : this.listMap){
        	double x = 0;
            double y = 0;
	        if(map !=null) {
	        	x = this.keyAxisX.get(0); 
	        	y = map.get((int)x); 
	        }
			for(Integer value : this.keyAxisX){
				drawFromOrigin(objGraphic,colors[color],x*auxX,y*auxY,value*auxX,map.get(value)*auxY);
				x = value;
				y = map.get(value);
			}
			color++;
        }  
    }
    
    /**
     * Draw a line from the color shown on the coordinates given as parameter. Also, draw the lines of the X and Y.
     * 
     * @param objGraphic      The specified Graphics context.
     * @param color      Color line.
     * @param fXini      X position in straight start (first coordinate).
     * @param fYini      Y position in straight start (first coordinate).
     * @param fZini      Z position in straight start (first coordinate).
     * @param fXfin      X position in finish of the line (second coordinate).
     * @param fYfin      Y position in finish of the line (second coordinate).
     * @param fZfin      Z position in finish of the line (second coordinate).
     */
    private void vDraw( Graphics objGraphic, Color color, double fXini, double fYini, double fZini, double fXfin, double fYfin, double fZfin)
    {
    	double fPuntoXini = (double) (iDisPantUser*fXini) / (iDisPantUser - fZini);
    	double fPuntoYini = (double) (iDisPantUser*-fYini) / (iDisPantUser - fZini);
    	double fPuntoXfin = (double) (iDisPantUser*fXfin) / (iDisPantUser - fZfin);
    	double fPuntoYfin = (double) (iDisPantUser*-fYfin) / (iDisPantUser - fZfin);
    	int iPuntoXini = (int) (fConstanteX * (fPuntoXini - fMinPantX));
    	int iPuntoXfin = (int) (fConstanteX * (fPuntoXfin - fMinPantX));
    	int iPuntoYini = (int) (fConstanteY * (fPuntoYini - fMinPantY));
    	int iPuntoYfin = (int) (fConstanteY * (fPuntoYfin - fMinPantY));

    	objGraphic.setColor(color);
    	objGraphic.drawLine(iPuntoXini, iPuntoYini, iPuntoXfin, iPuntoYfin);
        objGraphic.setColor(Color.BLACK);
        objGraphic.drawString("Y", 5, 15);
        objGraphic.drawString("X", 468, 273);
    }

    /**
     * Draw a line using the vDraw() method, normalized values taking into account 
     * the second quadrant of the Cartesian plane (X and Y axis positive)
     * 
     * @param objGraphic      The specified Graphics context.
     * @param color      Color line.
     * @param fXini      X axis position of the first ordered pair (on the origin 0.0).
     * @param fYini      Y axis position of the first ordered pair (on the origin 0.0).
     * @param fXfin      X axis position of the second ordered pair.
     * @param fYfin      Y axis position of the second ordered pair.
     */
    private void drawFromOrigin( Graphics objGraphic, Color color, double fXini, double fYini, double fXfin, double fYfin)
    {
        fYini *= 0.9;
        fYfin *= 0.9;
        fXini *= 0.93;
        fXfin *= 0.93;
        vDraw(objGraphic,color,fXini-var+8,fYini-var+8,0,fXfin-var+8,fYfin-var+8,0);
    }

    /**
     * Drawing the values of the independent variable (X axis) on the plane.
     *  
     * @param objGraphic      The specified Graphics context.
     */
    private void paintAxisX(Graphics objGraphic) {
        double a = this.maxXGraph;
        double aux = 401/a;
        objGraphic.drawString("0", 31, 273);

        double variable;
        for(int x : this.valuesAxisX){
            variable = (x*aux)+31;
            if(x==this.maxXGraph)
                objGraphic.drawString(String.valueOf(x), 432, 273);
            else
                objGraphic.drawString(String.valueOf(x), (int) Math.ceil(variable),273);
        }
    }

    /**
     * Drawing the values of the dependent variable (Y axis) on the plane.
     *  
     * @param objGraphic      The specified Graphics context.
     */
    private void paintAxisY(Graphics objGraphic) {
    	int aprox = (int) (this.maxYGraph*1000);
    	double increase = (double)aprox/10000;
    	double j = this.maxYGraph+increase;
        for(double i = 35; i<=240 ;i=i+22.5){
        	j -= increase; 
            objGraphic.drawString(""+Math.rint(j*1000)/1000, -1, (int) Math.rint(i));
        }
        objGraphic.drawString("0.000", -1, 260);
    }

    /**
     * Sets the values of the results as a map with lists of values.
     * 
     * @param listMap
     */
	public void setResultAlgorithm(List<Map<Integer, Double>> listMap){
		this.listMap = listMap;
	}

	/**
	 * Sets the maximum value of the dependent variable (Y axis).
	 * 
	 * @param maxY
	 */
	public void setMaxY(double maxY) {
        maxYGraph = maxY;
        varYGraph = 100/maxYGraph;		
	}
}